package com.zijidelu.luframework.utils.lang.reflect.bean;

import cn.hutool.core.util.StrUtil;
import com.zijidelu.luframework.utils.lang.reflect.TypeCompatibilityUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * 反射操作器接口。
 * <p>
 * 定义了对象创建、属性访问、字段访问和方法调用的统一契约。
 * 所有具体的反射实现（如 ReflectASM、JDK MethodHandle）都应实现此接口。
 *
 * @param <T> 目标对象的类型
 */
public interface ReflectAccessor<T> {
    String GET_PREFIX = "get";
    String SET_PREFIX = "set";
    String IS_PREFIX = "is";

    /**
     * 获取目标对象的类。
     *
     * @return 目标对象的类
     */
    Class<T> getObjClass();

    /**
     * 获取目标类的所有属性缓存。
     * <p>
     * 键为属性名，值为 BeanProperty 对象。
     *
     * @return 目标类的所有属性缓存
     */
    Map<String, BeanProperty> getBeanPropertyCache();

    /**
     * 创建目标类的实例（调用无参构造函数）。
     *
     * @return 新创建的实例
     */
    T newInstance();

    /**
     * 使用指定参数创建目标类的实例。
     *
     * @param args 构造函数参数
     * @return 新创建的实例
     */
    T newInstance(final Object... args);

    /**
     * 复制源对象的属性值到目标对象。
     * <li>原对象，目标对象要复制的字段必须实现getter，setter方法。
     * <li>如果目标对象没有实现getter，setter方法， * 则不会复制该字段。
     *
     * @param source 源对象
     * @param target 目标对象
     */
    void copyProperties(final Object source, final Object target);

    /**
     * 直接获取对象的字段值（绕过封装）。
     *
     * @param obj       目标对象
     * @param fieldName 字段名
     * @return 字段值
     */
    Object getFieldValue(final Object obj, final String fieldName);

    /**
     * 直接设置对象的字段值（绕过封装）。
     *
     * @param obj       目标对象
     * @param fieldName 字段名
     * @param value     要设置的值
     */
    void setFieldValue(final Object obj, final String fieldName, final Object value);

    /**
     * 调用对象的指定无参方法。
     *
     * @param obj        目标对象
     * @param methodName 方法名
     * @return 方法执行结果
     */
    Object invoke(final Object obj, final String methodName);

    /**
     * 调用对象的指定方法。
     *
     * @param obj        目标对象
     * @param methodName 方法名
     * @param args       参数列表
     * @return 方法执行结果
     */
    Object invoke(final Object obj, final String methodName, final Object... args);

    /**
     * 调用对象的指定方法（最精确的版本）。
     *
     * @param obj        目标对象
     * @param methodName 方法名
     * @param paramTypes 参数类型数组
     * @param args       参数列表
     * @return 方法执行结果
     */
    Object invoke(final Object obj, final String methodName, final Class<?>[] paramTypes, final Object... args);

    /**
     * 根据getter方法名计算对应的字段名
     *
     * @param methodName getter方法名，例如"getName"、"getAge"等
     * @return 对应的字段名，例如"name"、"age"等
     */
    static String calcFieldNameWithGetter(final String methodName) {
        // 从方法名中截除"get"前缀，返回剩余部分作为字段名
        return StrUtil.lowerFirst(methodName.substring(GET_PREFIX.length()));
    }

    /**
     * 计算字段名，通过去掉setter方法名的前缀得到
     *
     * @param methodName setter方法的名称
     * @return 去掉前缀后的字段名
     */
    static String calcFieldNameWithSetter(final String methodName) {
        // 从方法名中截取掉SET_PREFIX前缀部分，得到对应的字段名
        return StrUtil.lowerFirst(methodName.substring(SET_PREFIX.length()));
    }

    /**
     * 根据isXxx方法名计算对应的字段名
     *
     * @param methodName isXxx方法名，例如"isActive"、"isAdmin"等
     * @return 对应的字段名，例如"active"、"admin"等
     */
    static String calcFieldNameWithIs(final String methodName) {
        // 从方法名中截取掉IS_PREFIX前缀部分，得到对应的字段名
        return StrUtil.lowerFirst(methodName.substring(IS_PREFIX.length()));
    }

    /**
     * 计算Getter方法名称
     *
     * @param fieldName 字段名称
     * @return 对应的Getter方法名称
     */
    static String calcGetterName(final String fieldName) {
        // 返回"get"前缀与首字母大写的字段名称的组合
        if (fieldName == null || fieldName.isEmpty()) return null;

        int len = fieldName.length();
        char[] chars = new char[len + 3]; // "get" + fieldName
        chars[0] = 'g';
        chars[1] = 'e';
        chars[2] = 't';

        char first = fieldName.charAt(0);
        chars[3] = Character.toUpperCase(first);

        if (len > 1) {
            fieldName.getChars(1, len, chars, 4);
        }

        return new String(chars);
    }

    /**
     * 根据字段名计算对应的setter方法名
     *
     * @param fieldName 字段名，例如"userName"
     * @return setter方法名，例如"setUserName"
     */
    static String calcSetterName(final String fieldName) {
        // 将字段名的首字母大写，然后与"set"前缀拼接形成setter方法名
        if (fieldName == null || fieldName.isEmpty()) return null;

        int len = fieldName.length();
        char[] chars = new char[len + 3];
        // "set" + fieldName
        chars[0] = 's';
        chars[1] = 'e';
        chars[2] = 't';

        char first = fieldName.charAt(0);
        chars[3] = Character.toUpperCase(first);

        if (len > 1) {
            fieldName.getChars(1, len, chars, 4);
        }

        return new String(chars);
    }

    /**
     * 计算并返回一个"is"开头的getter方法名
     *
     * @param fieldName 字段名，用于生成对应的getter方法名
     * @return 返回"is"开头且首字母大写的方法名
     */
    static String calcIsGetterName(final String fieldName) {
        if (fieldName == null || fieldName.isEmpty()) return null;

        int len = fieldName.length();
        char[] chars = new char[len + 2];
        // "is" + fieldName
        chars[0] = 'i';
        chars[1] = 's';

        char first = fieldName.charAt(0);
        chars[2] = Character.toUpperCase(first);

        if (len > 1) {
            fieldName.getChars(1, len, chars, 3);
        }

        return new String(chars);
    }

    /**
     * 记录类，用于存储字段名、getter方法和setter方法
     *
     * @param fieldName 字段名
     * @param fieldType 字段类型
     * @param getter    获取字段值的方法
     * @param setter    设置字段值的方法
     */
    record BeanProperty(String fieldName, Class<?> fieldType, Function<Object, Object> getter,
                        BiConsumer<Object, Object> setter) {
    }

    /**
     * 记录类，用于缓存构造函数及其访问器
     *
     * @param paramTypesArray 构造函数参数类型数组
     * @param creator         创建实例的函数
     * @param isPublic        构造函数是否为 public
     */
    record BeanConstructor<T>(Class<?>[] paramTypesArray, Function<Object[], T> creator, boolean isPublic) {
    }

    /**
     * 记录类，用于存储源类、目标类和需要复制的属性列表
     *
     * @param sourceClass    源类
     * @param targetClass    目标类
     * @param beanProperties 需要复制的属性列表
     */
    record BeanCopier(Class<?> sourceClass, Class<?> targetClass, List<BeanProperty> beanProperties) {

        public static BeanCopier of(ReflectAccessor<?> source, ReflectAccessor<?> target) {
            // 源访问器需要getter方法，目标访问器需要setter方法
            final List<BeanProperty> beanProperties = new ArrayList<>();
            target.getBeanPropertyCache().forEach((fieldName, targetBeanProperty) -> {
                // 筛选出源访问器中存在的字段名
                final BeanProperty sourceBeanProperty = source.getBeanPropertyCache().get(fieldName);
                // 源访问器中不存在该字段，跳过
                if (sourceBeanProperty == null) {
                    return;
                }
                // 字段类型不匹配，跳过
                if (!TypeCompatibilityUtils.isAutoBoxingCompatible(
                        sourceBeanProperty.fieldType, targetBeanProperty.fieldType)) {
                    return;
                }

                // 构建BeanProperty对象，包含字段名、源访问器的getter方法和目标访问器的setter方法
                beanProperties.add(new BeanProperty(fieldName, sourceBeanProperty.fieldType, sourceBeanProperty.getter(), targetBeanProperty.setter()));
            });

            return new BeanCopier(source.getObjClass(), target.getObjClass(), beanProperties);
        }

    }

}
